package {
  import flash.display.Sprite;
  import flash.events.KeyboardEvent;
  import flash.text.TextField;
  import flash.text.TextFieldType;
  import flash.ui.Keyboard;
 
  import net.user1.reactor.*;

  public class UnionChat extends Sprite {
// =============================================================================
// APPLICATION VARIABLES
// =============================================================================

    // Union objects
    protected var reactor:Reactor;
    protected var chatRoom:Room;
   
    // User-interface objects
    protected var incomingMessages:TextField;
    protected var outgoingMessages:TextField;
    protected var userlist:TextField;
    protected var nameInput:TextField;
    protected var privateRecipientInput:TextField;
    protected var messageCounter:TextField;

// =============================================================================
// APPLICATION STARTUP
// =============================================================================

    // Constructor    
    public function UnionChat () {
      // Create the user interface
      buildUI();
      // Make the Reactor object, and load configuration settings from
      // an external file. The connection will open automatically after the
      // settings have loaded.
      reactor = new Reactor("config.xml");
      // Register for the READY event
      reactor.addEventListener(ReactorEvent.READY,
                               readyListener);
    }

// =============================================================================
// CONNECTION READY TASKS
// =============================================================================
   
    // Method invoked when the connection is ready
    protected function readyListener (e:ReactorEvent):void {
      // Assign the current user an auto-generated username
      setUserName("Guest"
                  + reactor.getClientManager().self().getClientID());
     
      // Register for private-chat messages from other users
      reactor.getMessageManager().addMessageListener("PRIVATE_MESSAGE",
                                                     privateMessageListener);
                                                       
      // Display a welcome message
      displayMessage("Connected to Union");
     
      // Create the settings for the chat room. We want the
      // room to exist until the server shuts down, so set
      // dieOnEmpty to false.
      var settings:RoomSettings = new RoomSettings();
      settings.dieOnEmpty = false;  
     
      // Create the chat room (if the room already exists, the
      // request will be ignored, but the Room reference will
      // still be valid)
      chatRoom = reactor.getRoomManager().createRoom(
                                             "chatRoomThree", settings);
      // Register for regular chat messages
      chatRoom.addMessageListener("CHAT_MESSAGE",
                                  chatMessageListener);
      // Register for room events
      chatRoom.addEventListener(RoomEvent.SYNCHRONIZE,
                                synchronizeRoomListener);
      chatRoom.addEventListener(RoomEvent.ADD_CLIENT,
                                addClientListener);
      chatRoom.addEventListener(RoomEvent.REMOVE_CLIENT,
                                removeClientListener);
      chatRoom.addEventListener(RoomEvent.UPDATE_CLIENT_ATTRIBUTE,
                                updateClientAttributeListener);
      chatRoom.addEventListener(RoomEvent.UPDATE_ROOM_ATTRIBUTE,
                                updateRoomAttributeListener);
      // Join the chat room
      chatRoom.join();
    }

// =============================================================================
// UI CREATION
// =============================================================================
   
    // Create the user interface
    protected function buildUI ():void {
      // Incoming chat messages
      incomingMessages = makeTextField(0, 0, 300, 200);
      incomingMessages.wordWrap = true;

      // Outgoing chat messages
      outgoingMessages = makeTextField(0, 210, 400, 20);
      outgoingMessages.type = TextFieldType.INPUT;
      outgoingMessages.addEventListener(KeyboardEvent.KEY_UP,
                                        outgoingKeyUpListener);
     
      // The list of users                                        
      userlist = makeTextField(310, 0, 90, 200);
     
      // Input field for setting user name
      nameInput = makeTextField(0, 240, 400, 20);
      nameInput.type = TextFieldType.INPUT;
      nameInput.addEventListener(KeyboardEvent.KEY_UP,
                                 nameKeyUpListener);

      // Displays the total messages sent in the chat
      messageCounter = makeTextField(0, 274, 120, 20, false, false);
      messageCounter.text = "Total Messages: ??";
      messageCounter.textColor = 0xFFFFFF;

      // Instructions for using private chat
      var privateRecipientLabel:TextField = makeTextField(130, 274, 180, 20,
                                                       false, false);
      privateRecipientLabel.text = "To private chat, enter user name here:";
      privateRecipientLabel.textColor = 0xFFFFFF;

      // Input field for setting private-chat recipient
      privateRecipientInput = makeTextField(310, 270, 90, 20);
      privateRecipientInput.type = TextFieldType.INPUT;
     
      addChild(incomingMessages);
      addChild(outgoingMessages);
      addChild(userlist);
      addChild(nameInput);
      addChild(messageCounter);
      addChild(privateRecipientLabel);
      addChild(privateRecipientInput);
    }

     // Helper function to build text fields
    protected function makeTextField (tx:Number = 0, ty:Number = 0,
                                      twidth:Number = 0, theight:Number = 0,
                                      border:Boolean = true,
                                      background:Boolean = true):TextField {
      var textField:TextField = new TextField();
      textField.x = tx;
      textField.y = ty;
      textField.width = twidth;
      textField.height = theight;
      textField.border = border;
      textField.background = background;
      return textField;
    }

// =============================================================================
// UI EVENT LISTENERS
// =============================================================================

    // Keyboard listener for outgoingMessages text field
    protected function outgoingKeyUpListener (e:KeyboardEvent):void {
      var privateChatRecipient:IClient;
     
      // When the user presses the ENTER key...
      if (e.keyCode == Keyboard.ENTER) {
        // If there's a private-chat recipient specified,
        // attempt to find the matching client
        if (privateRecipientInput.text != "") {
          privateChatRecipient =
            reactor.getClientManager().getClientByAttribute("USERNAME",
                                                               privateRecipientInput.text);
          if (privateChatRecipient == null) {
            displayMessage("Cound not send message to: '"
                           + privateRecipientInput.text
                           + "'. No such user.");
            return;
          }
        }
       
        // If there's no private-chat recipient,
        // send the message to everyone in the room
        if (privateChatRecipient == null) {
          chatRoom.sendMessage("CHAT_MESSAGE",
                               true,
                               null,
                               outgoingMessages.text);
          // Add one to the room's MESSAGE_COUNTER attribute
          chatRoom.setAttribute("MESSAGE_COUNTER", "%v+1", true, false, true);
        } else {
          // There's a valid private-chat recipient, so send
          // the message to that client only
          privateChatRecipient.sendMessage("PRIVATE_MESSAGE",
                                          outgoingMessages.text);
          displayMessage("You told " + getUserName(privateChatRecipient)
                         + ": " + outgoingMessages.text);
        }
        outgoingMessages.text = "";
      }
    }
   
    // Keyboard listener for nameInput
    protected function nameKeyUpListener (e:KeyboardEvent):void {
      // When the user presses the ENTER key...
      if (e.keyCode == Keyboard.ENTER) {
        // Assign the new user name
        setUserName(nameInput.text);
        nameInput.text = "";
      }
    }
   
// =============================================================================
// UNION MESSAGE LISTENERS
// =============================================================================

    // Method invoked when a regular chat message is received
    protected function chatMessageListener (fromClient:IClient,
                                            messageText:String
                                            ):void {
      displayMessage(getUserName(fromClient)
                    + " says: " + messageText);
    }
   
    // Method invoked when a private message is received
    protected function privateMessageListener (fromClient:IClient,
                                               messageText:String
                                               ):void {
      displayMessage("Private message from: "
                     + getUserName(fromClient)
                     + ": " + messageText);
    }
   
// =============================================================================
// ROOM EVENT LISTENERS
// =============================================================================
   
    // Method invoked when the room's client list and
    // attributes are synchronized and ready for use
    protected function synchronizeRoomListener (e:RoomEvent):void {
      updateUserList();
    }
   
    // Method invoked when a client joins the room
    protected function addClientListener (e:RoomEvent):void {
      displayMessage(getUserName(e.getClient())
                     + " joined the chat.");
      updateUserList();
    }
   
    // Method invoked when a client leave the room
    protected function removeClientListener (e:RoomEvent):void {
      displayMessage(getUserName(e.getClient())
                     + " left the chat.");
      updateUserList();
    }
   
    // Method invoked when a client
    // changes one of its shared attributes
    protected function updateClientAttributeListener (e:RoomEvent):void {
      var changedAttr:Attribute = e.getChangedAttr();
      // If the attribute that changed was USERNAME...
      if (changedAttr.name == "USERNAME") {
        // Display a message and update the user list
        if (changedAttr.oldValue != null) {
          displayMessage(changedAttr.oldValue
                         + "'s name changed to "
                         + getUserName(e.getClient()));
          updateUserList();
        }
      }
    }
   
    // Method invoked when any of the room's
    // shared room attributes change
    protected function updateRoomAttributeListener (e:RoomEvent):void {
      var changedAttr:Attribute = e.getChangedAttr();
      // If the attribute that changed was MESSAGE_COUNTER...
      if (changedAttr.name == "MESSAGE_COUNTER") {
        // Display the new message count
        messageCounter.text = "Total Messages: " + parseInt(e.getChangedAttr().value);
      }
    }
   
// =============================================================================
// USERNAME MANAGEMENT
// =============================================================================
   
    // Returns the specified client's username
    protected function getUserName (client:IClient):String {
      return client.getAttribute(null, "USERNAME");
    }
   
    // Assigns a new username to the current client
    protected function setUserName (userName:String):void {
      var self:IClient;
      // Check if the desired new name is valid
      if (userName == null || userName.length == 0) {
        return;
      }
      self = reactor.getClientManager().self();
      // Set the shared attribute so other clients will
      // be notified of the changed user name.
      self.setAttribute("USERNAME", userName);
    }
   
// =============================================================================
// UI CONTROL
// =============================================================================
   
    // Displays the client list on screen
    protected function updateUserList ():void {
      userlist.text = "";
      for each (var client:IClient in chatRoom.getClients()) {
        userlist.appendText(getUserName(client) + "\n");
      }
    }
   
    // Displays a message in the incoming text field
    protected function displayMessage (message:String):void {
      incomingMessages.appendText(message + "\n");
      incomingMessages.scrollV = incomingMessages.maxScrollV;
    }
  }
}